#ifndef _VECTOR3_HPP_
#define _VECTOR3_HPP_

#include "Vector2.hpp"

class Vector3 {
public:

	union {

		struct {
			
			float x;
		
			float y;
		
			float z;
			
		};
		
		struct {
		
			float pitch;
			
			float yaw;
			
			float roll;
			
		};
		
		struct {
		
			float r;
			
			float g;
			
			float b;
			
		};
		
	};

public:

	float* getPtr() {
		return (float*)this;
	}

	const float* getPtr() const {
		return (float*)this;
	}
	
	operator Vector2 () const {
		return Vector2(x, y);
	}
	
	Vector2 toVector2() const {
		return Vector2(x, y);
	}

	float& operator[] (unsigned int index) {
		return ((float*)this)[index];
	}
	
	Vector3 operator + (const Vector3& v) const {
		return Vector3(x + v.x, y + v.y, z + v.z);
	}
	
	Vector3 operator - (const Vector3& v) const {
		return Vector3(x - v.x, y - v.y, z - v.z);
	}

	Vector3 operator * (const Vector3& v) const {
		return Vector3(x * v.x, y * v.y, z * v.z);
	}

	Vector3 operator / (const Vector3& v) const {
		return Vector3(x / v.x, y / v.y, z / v.z);
	}
	
	void operator += (const Vector3& v) {
		x += v.x;
		y += v.y;
		z += v.z;
	}
	
	void operator -= (const Vector3& v) {
		x -= v.x;
		y -= v.y;
		z -= v.z;
	}

	void operator *= (const Vector3& v) {
		x *= v.x;
		y *= v.y;
		z *= v.z;
	}

	void operator /= (const Vector3& v) {
		x /= v.x;
		y /= v.y;
		z /= v.z;
	}

	Vector3 operator + (float s) const {
		return Vector3(x + s, y + s, z + s);
	}
	
	Vector3 operator - (float s) const {
		return Vector3(x - s, y - s, z - s);
	}

	Vector3 operator * (float s) const {
		return Vector3(x * s, y * s, z * s);
	}

	Vector3 operator / (float s) const {
		return Vector3(x / s, y / s, z / s);
	}
	
	void operator += (float s) {
		x += s;
		y += s;
		z += s;
	}
	
	void operator -= (float s) {
		x -= s;
		y -= s;
		z -= s;
	}

	void operator *= (float s) {
		x *= s;
		y *= s;
		z *= s;
	}

	void operator /= (float s) {
		x /= s;
		y /= s;
		z /= s;
	}
	
public:

	/*
		Длинна вектора
	*/

	float length() const {
		return sqrt(x * x + y * y + z * z);
	}
	
	/*
		Длинна вектора
	*/

	static float length(const Vector3& v) {
		return v.length();
	}
	
	/*
		Расстояние между двумя векторами
	*/
	
	static float distance(const Vector3& v1, const Vector3& v2) {
		return (v2 - v1).length();
	}
	
	/*
		Скалярное произведение (dot product)
	*/
	
	static float dot(const Vector3& v1, const Vector3& v2) {
		return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
	}
	
	/*
		Векторное произведение (cross product)
	*/
	
	static Vector3 cross(const Vector3& v1, const Vector3& v2) {
		return Vector3 (
			v1.y * v2.z - v1.z * v2.y,
			v1.z * v2.x - v1.x * v2.z,
			v1.x * v2.y - v1.y * v2.x
		);
	}
	
	/*
		Нормализация
	*/
	
	static Vector3 normalize(const Vector3& v) {
		return v * (1.0f / v.length());
	}
	
	/*
		Линейная интерполяция
	*/
	
	static Vector3 lerp(const Vector3& v1, const Vector3& v2, float a) {
		return Vector3(
			(v2.x - v1.x) * a + v1.x,
			(v2.y - v1.y) * a + v1.y,
			(v2.z - v1.z) * a + v1.z
		);
	}

public:

	Vector3(float x, const Vector2& v) {
		Vector3::x = x;
		Vector3::y = v.x;
		Vector3::z = v.y;
	}

	Vector3(const Vector2& v, float z = 0.0f) {
		Vector3::x = v.x;
		Vector3::y = v.y;
		Vector3::z = z;
	}
	
	Vector3(float s) {
		Vector3::x = s;
		Vector3::y = s;
		Vector3::z = s;
	}

	Vector3(float x, float y, float z = 0.0f) {
		Vector3::x = x;
		Vector3::y = y;
		Vector3::z = z;
	}

	Vector3() {
	}
	
};

#endif